package com.znjc.device.service;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.znjc.common.constant.DeviceConstants;
import com.znjc.common.core.redis.RedisCache;
import com.znjc.common.utils.StringUtils;
import com.znjc.device.domain.*;
import com.znjc.device.util.DataUtil;
import com.znjc.device.util.DateUtil;
import com.znjc.device.websocket.WebSocketUsers;
import com.znjc.system.service.ISysDictDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据存储服务
 * 负责将采集到的数据存储到数据库
 */
@Slf4j
@Service
public class DataStorageService {
    @Autowired
    private IIotDataResRuleService iotDataResRuleService;
    @Autowired
    private IDeviceRuleRefService deviceRuleRefService;
    @Autowired
    private ISysDictDataService sysDictDataService;
    @Autowired
    private IDevicePointService devicePointService;
    @Autowired
    private IDeviceDeviceService deviceDeviceService;
    @Autowired
    private IDeviceAlarmService deviceAlarmService;
    @Autowired
    private DeviceDataService deviceDataService;
    @Autowired
    private RedisCache redisCache;

    /**
     * 保存采集数据
     *
     * @param iotSerial 串口信息
     * @param dataCheck 采集命令
     * @param data      采集到的数据
     * @param date      采集时间
     */
    public void saveCollectionData(IotSerial iotSerial, IotDataCheck dataCheck, String data, Date date) {
        try {
            log.info("存储采集数据 - 串口: {}, 命令: {}, 数据: {}",
                    iotSerial.getSerialCode(), dataCheck.getCheckName(), data);
            WebSocketUsers.sendMessageToUserByText(DeviceConstants.VUE_MSG_CLINET_9996, "开始解析数据-串口:" + iotSerial.getSerialCode() + ", 命令:" + dataCheck.getCommand() + ", 数据: " + data);
            data = data.replaceAll("  ", " ").replaceAll("  ", " ");
            String[] dataArray = data.split(" ");
            List<String> dataList = new ArrayList<>(Arrays.asList(dataArray));
            if (CollectionUtils.isNotEmpty(dataList) && dataList.size() > dataCheck.getAcquireStartLen()) {
                List<String> analysisDatas = new ArrayList<>(dataList.subList(dataCheck.getAcquireStartLen() > 0 ? dataCheck.getAcquireStartLen() - 1 : 0, dataList.size()));
                if (CollectionUtils.isNotEmpty(analysisDatas)) {
                    List<IotDataResRule> resRules = iotDataResRuleService.getRules(dataCheck.getId());
                    if (CollectionUtils.isEmpty(resRules)) {
                        log.error("数据未配置解析规则，无法解析！接受数据为：" + data);
                        WebSocketUsers.sendMessageToUserByText(DeviceConstants.VUE_MSG_CLINET_9996, "数据未配置解析规则，无法解析！接受数据为：" + data);
                    }
                    //分割数据
                    List<List<String>> deviceDataList = new ArrayList<>();
                    int endIndex = dataCheck.getDataSliptNum();
                    for (int i = 0; i < analysisDatas.size(); ) {
                        deviceDataList.add(new ArrayList<>(analysisDatas.subList(i, endIndex)));
                        i += dataCheck.getDataSliptNum();
                        endIndex += dataCheck.getDataSliptNum();
                        if (analysisDatas.size() - i < dataCheck.getDataSliptNum()) {
                            deviceDataList.add(new ArrayList<>(analysisDatas.subList(i, analysisDatas.size())));
                            break;
                        }
                        if (analysisDatas.size() == i) {
                            break;
                        }
                    }
                    if (CollectionUtils.isEmpty(deviceDataList)) {
                        log.error("数据分割失败！数据为：" + data);
                        WebSocketUsers.sendMessageToUserByText(DeviceConstants.VUE_MSG_CLINET_9996, "数据分割失败！数据为：" + data);
                        return;
                    }
                    List<DeviceDevice> deviceDevices = new ArrayList<>();
                    List<DeviceAlarm> alarmList = new ArrayList<>();
                    List<DeviceData> deviceDatas = new ArrayList<>();
                    Map<String, String> redisDatas = new HashMap<>();
                    for (IotDataResRule rule : resRules) {
                        if (null != rule) {
                            if (rule.getDataNum() <= (deviceDataList.size() - 1)) {
                                List<String> deviceData = deviceDataList.get(rule.getDataNum() - 1);
                                if (CollectionUtils.isEmpty(deviceData)) {
                                    log.error("规则：" + rule.getRuleName() + ",得到的数据为空");
                                    WebSocketUsers.sendMessageToUserByText(DeviceConstants.VUE_MSG_CLINET_9996, "规则：" + rule.getRuleName() + ",得到的数据为空");
                                    continue;
                                }
                                //获取测点id
                                Long pointId = deviceRuleRefService.getPointId(rule.getDeviceId(), rule.getId());
                                if (null != pointId) {
                                    //数据高低位转换
                                   /* if (null != rule.getIsHighLow() && 1 == rule.getIsHighLow()) {
                                        Collections.reverse(deviceData);
                                    }*/
                                    //解析数据
                                    DeviceData deviceData1 = ruleData(rule, deviceData, rule.getDeviceId(), pointId, date);
                                    if (null != deviceData1) {
                                        deviceDatas.add(deviceData1);
                                        DeviceDevice deviceBase = deviceDeviceService.getById(rule.getDeviceId());
                                        if (null != deviceBase.getJcqdz()) {
                                            redisDatas.put(DeviceConstants.DEVICE_VALUE + deviceBase.getJcqdz(), deviceData1.getEquivalentValue());
                                        }
                                        //告警判断
                                        if (StringUtils.isNotEmpty(rule.getThresholdOperator()) && null != rule.getThresholdValue()) {
                                            DevicePoint depotPoint = devicePointService.getById(pointId);
                                            String thresholdOperators = sysDictDataService.selectDictLabel(DeviceConstants.THRESHOLD_OPERATOR, rule.getThresholdOperator());
                                            DeviceAlarm alarm = DataUtil.packAlarm(rule, deviceData1.getEquivalentValue(), deviceBase, depotPoint, thresholdOperators);
                                            if (null != alarm) {
                                                alarmList.add(alarm);
                                                deviceBase.setDeviceStatus("1");
                                            } else {
                                                deviceBase.setDeviceStatus("0");
                                            }
                                        }
                                        deviceDevices.add(deviceBase);

                                    }
                                }
                            }
                        }
                    }
                    if (CollectionUtils.isNotEmpty(deviceDevices)) {
                        deviceDeviceService.saveBatch(deviceDevices);
                    }
                    if (CollectionUtils.isNotEmpty(alarmList)) {
                        deviceAlarmService.saveBatch(alarmList);
                    }
                    if (CollectionUtils.isNotEmpty(deviceDatas)) {
                        deviceDataService.saveDeviceDatas(deviceDatas);
                    }
                    if (CollectionUtils.isNotEmpty(redisDatas)) {
                        for (String key : redisDatas.keySet()) {
                            redisCache.redisTemplate.opsForValue().set(key, redisDatas.get(key));
                        }
                    }
                    WebSocketUsers.sendMessageToUserByText(DeviceConstants.VUE_MSG_CLINET_9996, "存储采集数据完成");
                }
            }

        } catch (Exception e) {
            log.error("存储采集数据异常: {}" + e.getMessage(), e);
            WebSocketUsers.sendMessageToUserByText(DeviceConstants.VUE_MSG_CLINET_9996, "存储采集数据异常: {}" + e.getMessage());
        }
    }

    /**
     * 处理数据
     *
     * @param resRule
     * @param deviceData
     * @param deviceId
     * @param pointId
     * @param date
     */
    private DeviceData ruleData(IotDataResRule resRule, List<String> deviceData, Long deviceId, Long pointId, Date date) throws Exception {
        String dataTypes = sysDictDataService.selectDictLabel(DeviceConstants.DATA_TYPE, resRule.getDataType());
        String data = deviceData.stream().collect(Collectors.joining());
        String actualValue = "";
        //十进制数据判断，只要不是配置的10进制数据，均转换为10进制数据
        if (dataTypes.contains("二进制")) {
            BigInteger sint = new BigInteger(data, 16);
            //10进制转2进制
            data = sint.toString(2);
        } else if (dataTypes.contains("十六进制")) {
            data = String.valueOf(Integer.parseInt(data, 16));
        }
        actualValue = data;
        //计算data值
        //运算符
        String operatorUnit = sysDictDataService.selectDictLabel(DeviceConstants.OPERATOR_UNIT, resRule.getOperatorUnit());
        data = DataUtil.getDataValue(resRule, data, operatorUnit);
        //告警处理
        if ("Boolean".equals(dataTypes)) {
            //设备告警
            //Boolean类型为开关量
            //通信告警
        } else if (dataTypes.contains("二进制")) {
            //告警
            String dataBit = data;
        }
        return packDeviceData(data, resRule, deviceId, pointId, date, actualValue);
    }

    private DeviceData packDeviceData(String data, IotDataResRule resRule, Long deviceId, Long pointId, Date date, String actualValue) throws Exception {
        DeviceData deviceData = new DeviceData();
        deviceData.setDeviceId(deviceId);
        deviceData.setPointId(pointId);
        deviceData.setActualValue(actualValue);
        deviceData.setEquivalentValue(data);
        deviceData.setDataUnit(sysDictDataService.selectDictLabel(DeviceConstants.DATA_UNIT, resRule.getDataUnit()));
        deviceData.setTs(DateUtil.DateToTimestamp(date));
        deviceData.setPoint(devicePointService.getById(pointId));
        DeviceDevice device = deviceDeviceService.getById(deviceId);
        deviceData.setDeviceName(device.getDeviceName());
        /*if(null != device){
            if(device.getDeviceName().contains("电阻")){
                deviceData.setDataType(0);
            }else{
                deviceData.setDataType(1);
            }
        }*/
        return deviceData;
    }
}